package com.csjbot.snowbot_rogue.camera;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;
import android.media.CamcorderProfile;
import android.media.FaceDetector;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;

import com.android.core.entry.Static;
import com.csjbot.csjbase.log.Csjlogger;
import com.csjbot.snowbot_rogue.R;
import com.csjbot.snowbot_rogue.camera.listener.CsjFaceDetectorListener;
import com.csjbot.snowbot_rogue.camera.util.CamParaUtil;
import com.csjbot.snowbot_rogue.camera.util.FileUtil;
import com.csjbot.snowbot_rogue.camera.util.ImageUtil;
import com.csjbot.snowbot_rogue.utils.CSJToast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Copyright (c) 2017, SuZhou CsjBot. All Rights Reserved.
 * www.csjbot.com
 * <p>
 * Created by 浦耀宗 at 2017/01/09 0018-14:32.
 * Email: puyz@csjbot.com
 */
public class CameraInterface {
    private static final String TAG = "puyz";
    private static CameraInterface mCameraInterface;
    private static final int MAX_FACE_NUM = 10;//最大可以检测出的人脸数量

    private Camera mCamera;
    private Camera.Parameters mParams;
    private MediaRecorder mediaRecorder;
    private Size mPreviewSize = null;
    private String currentFileName;
    private String mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/csjbot/video/";
    private SurfaceHolder mHolder;
    private boolean isPreviewing = false;
    private boolean mIsRecording;
    private byte[] mPreBuffer = null;
    private int mCameraId = -1;
    private int orientionOfCamera;   //前置摄像头layout角度
    private boolean isFaceDectect = false;

//
//    @Override
//    public void onPreviewFrame(byte[] data, Camera camera) {
////        if (mPreBuffer == null) {
////            int size = mPreviewSize.width * mPreviewSize.height *
////                    ImageFormat.getBitsPerPixel(mParams.getPreviewFormat()) / 8;
////            mPreBuffer = new byte[size];
////        }
////        mCamera.addCallbackBuffer(mPreBuffer);
//        Camera.Size localSize = camera.getParameters().getPreviewSize();  //获得预览分辨率
//        YuvImage localYuvImage = new YuvImage(data, ImageFormat.NV21, localSize.width, localSize.height, null);
//        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
//        //把摄像头回调数据转成YUV，再按图像尺寸压缩成JPEG，从输出流中转成数组
//        localYuvImage.compressToJpeg(new Rect(0, 0, localSize.width, localSize.height), 60, localByteArrayOutputStream);
//        byte[] arrayOfByte = localByteArrayOutputStream.toByteArray();
//        cameraRelease();//及早释放camera资源，避免影响camera设备的正常调用
//        if (isFaceDectect) {
//            storeByteImage(arrayOfByte);
//        }
//    }

    private void cameraRelease() {
    }


    private void stopDetct() {
        if (null != mCamera) {
            mCamera.setPreviewCallbackWithBuffer(null);
        }
    }


    private void setCameraDisplayOrientation(Context contex, int paramInt, Camera paramCamera) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(paramInt, info);
        int rotation = ((WindowManager) contex.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();  //获得显示器件角度
        int degrees = 0;
        Csjlogger.info("getRotation's rotation is " + String.valueOf(rotation));
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        orientionOfCamera = info.orientation;      //获得摄像头的安装旋转角度
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }

        //注意前后置的处理，前置是映象画面，该段是SDK文档的标准DEMO
        paramCamera.setDisplayOrientation(result);
    }

    private CsjFaceDetectorListener faceDetectorListener;

    public void setCsjFaceDetectorListener(CsjFaceDetectorListener listener) {
        this.faceDetectorListener = listener;
    }

    private void storeByteImage(final byte[] paramArrayOfByte) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Bitmap localBitmap1 = BitmapFactory.decodeByteArray(paramArrayOfByte, 0, paramArrayOfByte.length);
                int i = localBitmap1.getWidth();
                int j = localBitmap1.getHeight();   //从上步解出的JPEG数组中接出BMP，即RAW->JPEG->BMP
                Matrix localMatrix = new Matrix();

                Bitmap localBitmap2 = null;
                FaceDetector localFaceDetector = null;
                Csjlogger.debug("localBitmap1 {} {} ", String.valueOf(localBitmap1.getWidth()), localBitmap1.getHeight());

                //根据前置安装旋转的角度来重新构造BMP
                switch (orientionOfCamera) {
                    case 0:
                        localFaceDetector = new FaceDetector(i, j, MAX_FACE_NUM);
                        localMatrix.postRotate(0.0F, i / 2, j / 2);
                        localBitmap2 = Bitmap.createBitmap(i, j, Bitmap.Config.RGB_565);
                        break;
                    case 90:
                        localFaceDetector = new FaceDetector(j, i, MAX_FACE_NUM);   //长宽互换
                        localMatrix.postRotate(-270.0F, j / 2, i / 2);  //正90度的话就反方向转270度，一样效果
                        localBitmap2 = Bitmap.createBitmap(i, j, Bitmap.Config.RGB_565);
                        break;
                    case 180:
                        localFaceDetector = new FaceDetector(i, j, MAX_FACE_NUM);
                        localMatrix.postRotate(-180.0F, i / 2, j / 2);
                        localBitmap2 = Bitmap.createBitmap(i, j, Bitmap.Config.RGB_565);
                        break;
                    case 270:
                        localFaceDetector = new FaceDetector(j, i, MAX_FACE_NUM);
                        localMatrix.postRotate(-90.0F, j / 2, i / 2);
                        localBitmap2 = Bitmap.createBitmap(j, i, Bitmap.Config.RGB_565);  //localBitmap2应是没有数据的
                        break;
                    default:
                        break;
                }
                FaceDetector.Face[] arrayOfFace = new FaceDetector.Face[MAX_FACE_NUM];
                Canvas localCanvas = new Canvas();
                localCanvas.setBitmap(localBitmap2);
                localCanvas.setMatrix(localMatrix);
                localCanvas.drawBitmap(localBitmap1, 0.0F, 0.0F, localPaint1); //该处将localBitmap1和localBitmap2关联（可不要？）

                if (localBitmap2 == null) {
                    return;
                }

                int numberOfFaceDetected = localFaceDetector.findFaces(localBitmap2, arrayOfFace);
//        localBitmap2.recycle();
                localBitmap1.recycle();   //释放位图资源

                FaceDetector.Face[] realFaces = new FaceDetector.Face[numberOfFaceDetected];
                int realFaceNum = 0;
                for (FaceDetector.Face face : arrayOfFace) {
                    if (face != null && face.confidence() > 0.51f) {
                        realFaces[realFaceNum] = face;
                        realFaceNum++;
                    }
                }
                Csjlogger.info("realFaces" + realFaceNum + "face");

                if (faceDetectorListener != null && isFaceDectect) {
                    faceDetectorListener.onFaceDetected(realFaces, realFaceNum, localBitmap2);
                }
            }

        }).start();

    }

    public boolean isIsRecording() {
        return mIsRecording;
    }

    public interface CamOpenOverCallback {
        void cameraHasOpened();
    }

    private Paint localPaint1;


    private CameraInterface() {
        localPaint1 = new Paint();
        localPaint1.setDither(true);
    }

    public static synchronized CameraInterface getInstance() {
        if (mCameraInterface == null) {
            mCameraInterface = new CameraInterface();
        }
        return mCameraInterface;
    }

    public void setVideoRecordPath(@NonNull String path) {
        this.mPath = path;
    }

    public void doStopVideoRecord() throws IOException {
        if (mediaRecorder != null) {
            if (mIsRecording) {
                try {
                    mediaRecorder.setOnErrorListener(null);
                    mediaRecorder.setOnInfoListener(null);
                    mediaRecorder.setPreviewDisplay(null);
                    mediaRecorder.stop();
                    mediaRecorder.release();
                } catch (IllegalStateException e) {
                    // TODO: handle exception
                    Csjlogger.info("Exception", Log.getStackTraceString(e));
                } catch (RuntimeException e) {
                    // TODO: handle exception
                    Csjlogger.info("Exception", Log.getStackTraceString(e));
                    File file = new File(currentFileName + ".3gp");
                    if (file.exists()) {
                        file.delete();
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                    Csjlogger.info("Exception", Log.getStackTraceString(e));
                } finally {
                    mediaRecorder = null;
                    mIsRecording = false;
                    try {
                        mCamera.reconnect();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw new IOException(e.getMessage());
                    }
                }

            }
        }
    }

    private static final boolean USE_133_SCREEN = true;

    public void doStartVideoRecord() throws IOException, IllegalStateException {
        try {
            mCamera.unlock();
            if (USE_133_SCREEN) {
                initVideoRecord();
            } else {
                initVideoRecord_old();
            }
            mediaRecorder.prepare();
            mediaRecorder.start();
            mIsRecording = true;
            Csjlogger.debug("doStartVideoRecord");
            takePictureForRecord();
        } catch (IOException e) {
            mCamera.lock();
            throw new IOException(e.getMessage());
        } catch (IllegalStateException e) {
            mCamera.lock();
            throw new IllegalStateException(e.getMessage());
        }
    }

    private void takePictureForRecord() {
        mCamera.takePicture(new ShutterCallback() {
            @Override
            public void onShutter() {

            }
        }, null, null, new PictureCallback() {
            /**
             * Called when image data is available after a picture is taken.
             * The format of the data depends on the context of the callback
             * and {@link Camera.Parameters} settings.
             *
             * @param data   a byte array of the picture data
             * @param camera the Camera service object
             */
            @Override
            public void onPictureTaken(final byte[] data, Camera camera) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        try {
                            saveFile(bitmap, currentFileName + ".jpg");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }

    @Deprecated
    public void initVideoRecord_old() {
        if (mediaRecorder == null) {
            Csjlogger.debug("initVideoRecord");
            mIsRecording = true;
            mediaRecorder = new MediaRecorder();
            mediaRecorder.reset();
            mediaRecorder.setCamera(mCamera);
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
            mediaRecorder.setOutputFile(getName());
            mediaRecorder.setVideoFrameRate(25);
            mediaRecorder.setVideoSize(640, 480);
//        CamcorderProfile mCamcorderProfile = CamcorderProfile.get(CameraInfo.CAMERA_FACING_BACK, CamcorderProfile.QUALITY_LOW);
//        mediaRecorder.setProfile(mCamcorderProfile);
            mediaRecorder.setPreviewDisplay(mHolder.getSurface());
        }
    }

    public void initVideoRecord() {
        if (mediaRecorder == null) {
            Csjlogger.debug("initVideoRecord");
            mIsRecording = true;

            CamcorderProfile mProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH);

            //1st. Initial state
            mediaRecorder = new MediaRecorder();
            mediaRecorder.setCamera(mCamera);

            //2st. Initialized state

            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);

            //3st. config
            mediaRecorder.setOutputFormat(mProfile.fileFormat);
            mediaRecorder.setAudioEncoder(mProfile.audioCodec);
            mediaRecorder.setVideoEncoder(mProfile.videoCodec);
            mediaRecorder.setOutputFile(getName());
            mediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
            mediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
            mediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
            mediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate);
            mediaRecorder.setAudioChannels(mProfile.audioChannels);
            mediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);

            mediaRecorder.setPreviewDisplay(mHolder.getSurface());
        }
    }

    /**
     * 保存文件
     *
     * @param bm
     * @param fileName
     * @throws IOException
     */
    public void saveFile(Bitmap bm, String fileName) throws IOException {
        File myCaptureFile = new File(fileName);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
        bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
        bos.flush();
        bos.close();
    }


    public void createFolder(String folderPath) {
        File file = new File(folderPath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private String getName() {
        createFolder(mPath);
        // 如 {SDCARD_PATH}/csjbot/video/1472475030.3pg
        currentFileName = mPath + System.currentTimeMillis();
        String fileName = currentFileName + ".3gp";
        return fileName;
    }

    /**
     * 打开Camera
     *
     * @param callback
     */
    public void doOpenCamera(CamOpenOverCallback callback, int cameraId) {
        try {
            Csjlogger.debug("camera num is" + Camera.getNumberOfCameras());
            mCamera = Camera.open(cameraId);
        } catch (Exception e) {
            CSJToast.showToast(Static.CONTEXT, Static.CONTEXT.getString(R.string.camera_error));
            Csjlogger.error(Static.CONTEXT.getString(R.string.camera_error), e);
        }

        mCameraId = cameraId;
        if (callback != null) {
            callback.cameraHasOpened();
        }
    }

    /**
     * 开启预览
     *
     * @param holder      {@link SurfaceHolder }
     * @param previewRate
     */
    public void doStartPreview(Context context, SurfaceHolder holder, float previewRate) {
        Csjlogger.info("doStartPreview...");
        if (isPreviewing) {
            mCamera.stopPreview();
            return;
        }
        mHolder = holder;

        if (mCamera != null) {
            mParams = mCamera.getParameters();
            mParams.setPictureFormat(ImageFormat.JPEG);//设置拍照后存储的图片格式
            CamParaUtil.getInstance().printSupportPictureSize(mParams);
            CamParaUtil.getInstance().printSupportPreviewSize(mParams);
            //设置PreviewSize和PictureSize
            Size pictureSize = CamParaUtil.getInstance().getPropPictureSize(
                    mParams.getSupportedPictureSizes(), previewRate, 640);

            mParams.setPictureSize(pictureSize.width, pictureSize.height);

            Size previewSize = CamParaUtil.getInstance().getPropPreviewSize(
                    mParams.getSupportedPreviewSizes(), previewRate, 640);
            mParams.setPreviewSize(previewSize.width, previewSize.height);

            String preWidth = mParams.get("preview_capture-size-width");
            String preHeight = mParams.get("preview_capture-size-height");
            String picWidth = mParams.get("capture-size-width");
            String picHeight = mParams.get("capture-size-height");
            try {
                int preViewWidth = Integer.valueOf(preWidth);
                int preViewHeight = Integer.valueOf(preHeight);
                int viewWidth = Integer.valueOf(picWidth);
                int viewHidth = Integer.valueOf(picHeight);

//                mParams.setPictureSize(viewWidth, viewHidth);
//                mParams.setPreviewSize(preViewWidth, preViewHeight);
            } catch (Exception e) {
                e.printStackTrace();
            }

            // TODO 提高fps
            mParams.setRecordingHint(true);
            mParams.setAutoExposureLock(true);
            mParams.setAutoWhiteBalanceLock(true);

            setCameraDisplayOrientation(context, mCameraId, mCamera);

            CamParaUtil.getInstance().printSupportFocusMode(mParams);
//            List<String> focusModes = mParams.getSupportedFocusModes();
//            if (focusModes.contains("continuous-video")) {
//                mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//            }
            mCamera.setParameters(mParams);
//            if (isFaceDectect) {
//                mPreviewSize = mParams.getPreviewSize();
////                if (mPreBuffer == null) {
////                    int size = mPreviewSize.width * mPreviewSize.height *
////                            ImageFormat.getBitsPerPixel(mParams.getPreviewFormat()) / 8;
////                    mPreBuffer = new byte[size];
////                }
////                mCamera.addCallbackBuffer(mPreBuffer);
//                mCamera.setPreviewCallback(this);
////                mCamera.setPreviewCallbackWithBuffer(this);
//            }Z

            isPreviewing = true;

            mParams = mCamera.getParameters(); //重新get一次
            Csjlogger.info("最终设置:PreviewSize--With = " + mParams.getPreviewSize().width
                    + "Height = " + mParams.getPreviewSize().height);
            Csjlogger.info("最终设置:PictureSize--With = " + mParams.getPictureSize().width
                    + "Height = " + mParams.getPictureSize().height);

//            mCamera.setPreviewCallback(this);
//            mCamera.setPreviewCallbackWithBuffer(this);

            try {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();//开启预览
            } catch (IOException e) {
                Csjlogger.error(e);
//                e.printStackTrace();
            }
        }
    }


    /**
     * 停止预览，释放Camera
     */
    public void doStopCamera() {
        if (null != mCamera) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            isPreviewing = false;
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 拍照
     */
    public void doTakePicture() {
        if (isPreviewing && (mCamera != null)) {
            mCamera.takePicture(mShutterCallback, null, mJpegPictureCallback);
        }
    }

    /**
     * 获取Camera.Parameters
     *
     * @return 返回相机的参数
     */
    public Camera.Parameters getCameraParams() {
        if (mCamera != null) {
            mParams = mCamera.getParameters();
            return mParams;
        }
        Csjlogger.error(this.getClass().getSimpleName(), "mParams is null");
        return null;
    }

    /**
     * 获取Camera实例
     *
     * @return 返回相机对象
     */
    public Camera getCameraDevice() {
        return mCamera;
    }

    public void startFaceDetection() {
        isFaceDectect = true;
    }

    public void setFaceDetection(boolean isFaceDectect) {
        this.isFaceDectect = isFaceDectect;
    }


    public int getCameraId() {
        return mCameraId;
    }


    /**
     * 为了实现拍照的快门声音及拍照保存照片需要下面三个回调变量
     * 快门按下的回调，在这里我们可以设置类似播放“咔嚓”声之类的操作。默认的就是咔嚓。
     */
    ShutterCallback mShutterCallback = new ShutterCallback() {
        public void onShutter() {
            Csjlogger.info("myShutterCallback:onShutter...");
        }
    };

    /**
     * 拍摄的未压缩原数据的回调,可以为null
     */
    PictureCallback mRawCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            Csjlogger.info("myRawCallback:onPictureTaken...");

        }
    };

    /**
     * 对jpeg图像数据的回调,最重要的一个回调
     */
    PictureCallback mJpegPictureCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            Csjlogger.info("myJpegCallback:onPictureTaken...");
            Bitmap b = null;
            if (null != data) {
                b = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据，将其解析成位图
                mCamera.stopPreview();
                isPreviewing = false;
            }
            //保存图片到sdcard
            if (null != b) {
                //设置FOCUS_MODE_CONTINUOUS_VIDEO)之后，myParam.set("rotation", 90)失效。
                //图片竟然不能旋转了，故这里要旋转下
                Bitmap rotaBitmap = ImageUtil.getRotateBitmap(b, 0);
                FileUtil.saveBitmap(rotaBitmap);
            }
            //再次进入预览
            mCamera.startPreview();
            isPreviewing = true;
        }
    };
}
